package com.xhj.mybatis.builder;

import java.util.HashMap;

/**
 * ParameterExpression
 *
 * @author XJks
 * @description 内联参数表达式解析器。
 * <p>
 * 支持的语法（简化版）：
 * <pre>
 * inline-parameter = (propertyName | expression) oldJdbcType attributes
 * propertyName = /expression language's property navigation path/
 * expression = '(' /expression language's expression/ ')'
 * oldJdbcType = ':' /any valid jdbc type/
 * attributes = (',' attribute)*
 * attribute = name '=' value
 * </pre>
 */
public class ParameterExpression extends HashMap<String, String> {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = 1L;

    /**
     * 构造方法，接收一个参数表达式字符串并解析
     *
     * @param expression 参数表达式
     */
    public ParameterExpression(String expression) {
        parse(expression);
    }

    /**
     * 解析参数表达式
     *
     * @param expression 参数表达式
     */
    private void parse(String expression) {
        // 跳过前导空白字符，获取第一个非空白字符的位置
        int p = skipWS(expression, 0);
        // 如果第一个非空白字符是左括号，说明是表达式，否则是属性名
        if (expression.charAt(p) == '(') {
            expression(expression, p + 1);
        } else {
            property(expression, p);
        }
    }

    /**
     * 解析括号表达式
     *
     * @param expression 参数表达式
     * @param left       左括号后的起始位置
     */
    private void expression(String expression, int left) {
        int match = 1; // 用于括号匹配计数
        int right = left + 1;
        // 循环查找与起始左括号匹配的右括号
        while (match > 0) {
            if (expression.charAt(right) == ')') {
                match--;
            } else if (expression.charAt(right) == '(') {
                match++;
            }
            right++;
        }
        // 截取括号内的表达式内容，存入map
        put("expression", expression.substring(left, right - 1));
        // 继续解析jdbc类型或属性
        jdbcTypeOpt(expression, right);
    }

    /**
     * 解析属性名
     *
     * @param expression 参数表达式
     * @param left       起始位置
     */
    private void property(String expression, int left) {
        if (left < expression.length()) {
            // 查找下一个逗号或冒号的位置
            int right = skipUntil(expression, left, ",:");
            // 截取属性名并存入map
            put("property", trimmedStr(expression, left, right));
            // 继续解析jdbc类型或属性
            jdbcTypeOpt(expression, right);
        }
    }

    /**
     * 跳过空白字符，返回第一个非空白字符的位置
     *
     * @param expression 字符串
     * @param p          起始位置
     * @return 第一个非空白字符的位置
     */
    private int skipWS(String expression, int p) {
        for (int i = p; i < expression.length(); i++) {
            if (expression.charAt(i) > 0x20) { // 0x20是空格
                return i;
            }
        }
        return expression.length();
    }

    /**
     * 查找字符串中第一个属于endChars的字符位置
     *
     * @param expression 字符串
     * @param p          起始位置
     * @param endChars   结束字符集合
     * @return 第一个匹配字符的位置
     */
    private int skipUntil(String expression, int p, final String endChars) {
        for (int i = p; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if (endChars.indexOf(c) > -1) {
                return i;
            }
        }
        return expression.length();
    }

    /**
     * 解析可选的jdbc类型或属性
     *
     * @param expression 参数表达式
     * @param p          起始位置
     */
    private void jdbcTypeOpt(String expression, int p) {
        p = skipWS(expression, p);
        if (p < expression.length()) {
            if (expression.charAt(p) == ':') {
                jdbcType(expression, p + 1);
            } else if (expression.charAt(p) == ',') {
                option(expression, p + 1);
            } else {
                // 既不是':'也不是','，说明语法错误
                throw new RuntimeException("Parsing error in {" + expression + "} in position " + p);
            }
        }
    }

    /**
     * 解析jdbc类型
     *
     * @param expression 参数表达式
     * @param p          起始位置
     */
    private void jdbcType(String expression, int p) {
        int left = skipWS(expression, p);
        int right = skipUntil(expression, left, ",");
        if (right > left) {
            // 截取jdbc类型并存入map
            put("jdbcType", trimmedStr(expression, left, right));
        } else {
            throw new RuntimeException("Parsing error in {" + expression + "} in position " + p);
        }
        // 继续解析属性
        option(expression, right + 1);
    }

    /**
     * 递归解析属性对（name=value）
     *
     * @param expression 参数表达式
     * @param p          起始位置
     */
    private void option(String expression, int p) {
        int left = skipWS(expression, p);
        if (left < expression.length()) {
            // 查找等号，获取属性名
            int right = skipUntil(expression, left, "=");
            String name = trimmedStr(expression, left, right);
            left = right + 1;
            // 查找逗号，获取属性值
            right = skipUntil(expression, left, ",");
            String value = trimmedStr(expression, left, right);
            // 存入map
            put(name, value);
            // 递归解析下一个属性
            option(expression, right + 1);
        }
    }

    /**
     * 截取字符串并去除首尾空白
     *
     * @param str   字符串
     * @param start 起始位置
     * @param end   结束位置
     * @return 去除空白后的字符串
     */
    private String trimmedStr(String str, int start, int end) {
        while (str.charAt(start) <= 0x20) {
            start++;
        }
        while (str.charAt(end - 1) <= 0x20) {
            end--;
        }
        return start >= end ? "" : str.substring(start, end);
    }

}
